# -*- coding: utf8 -*-
#
# Copyright (c) 2009 Matías Ribecky <status-notifications@mribecky.com.ar>
#
# This file is part of status-notifications.
# 
# status-notifications is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# status-notifications is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with status-notifications.  If not, see <http://www.gnu.org/licenses/>.

'''Manages the configuration.'''

import os
import copy
import logging
import ConfigParser

import keyring
import argparse
import xdg.BaseDirectory

from statusnotifications.notifier import Notifier
from statusnotifications.config import tool as cfg_tool

LOGGER = logging.getLogger('statusnotifications.config')
NOTIFIER_SECTION_NAME = 'notifier'
KEYRING_SERVICE_NAME = 'status-notifier'
_DEBUG_LEVELS = {'ERROR': logging.ERROR,
                 'WARNING': logging.WARNING,
                 'INFO': logging.INFO,
                 'DEBUG': logging.DEBUG}
DEFAULT_CONFIG = {'times': 1,
                  'timeout': 15,
                  'sleep': 5,
                  'notifier': 'libnotify',
                  'accounts': set()
                  }

class ConfigurationError(Exception):
    pass

class Account(object):
    '''An account model.'''

    def __init__(self, name, provider, username):
        self.name = name
        self.provider = provider
        self.username = username
        self.save_key = "%s-%s" % (self.name, self.username)

    def _get_password(self):
        '''Looks up the password in the keyring.'''

        return keyring.get_password(KEYRING_SERVICE_NAME, self.username)

    def _set_password(self, password):
        '''Sets the password in the keyring.'''

        keyring.set_password(KEYRING_SERVICE_NAME, self.username, password)

    password = property(_get_password, _set_password)

    def create_provider(self):
        '''Create a StatusProvider for the account.'''

        provider = self.provider(self.username, self.password)
        return provider

def load(argv, plug_mgr):
    '''Load the configuration.'''

    parser = _create_arg_parser()
    args = parser.parse_args(argv[1:])

    if args.debug:
        logging.basicConfig(level=_DEBUG_LEVELS[args.debug])

    cfg = copy.deepcopy(DEFAULT_CONFIG)

    LOGGER.info('Loading configuration')
    _load_config_files(cfg, plug_mgr)
    _load_config_args(args, cfg)

    if args.config:
        wnd = cfg_tool.ConfigWindow(cfg, plug_mgr)
        wnd.show_all()
        cfg_tool.gtk.main()
        _save_config(cfg)
        print cfg

    LOGGER.debug("Using '%s' notifier" % cfg['notifier'])
    notifier = Notifier.get_plugin(cfg['notifier'])(cfg)

    providers = set()
    for account in cfg['accounts']:
        LOGGER.debug("Using %s account: %s" % (account.provider.name,
                                               account.username))
        try:
            providers.add(account.create_provider())
        except plug_mgr.get_interface('StatusProviderError'):
            LOGGER.info("Account %s failed" % account.username)

    return providers, notifier, cfg['sleep']

def _load_config_files(cfg, plug_mgr):
    '''Loads the config from the filesystem.

    :return: A dict with the keys username and password
    '''

    LOGGER.info('Reading configuration files')

    cfg_parser = ConfigParser.ConfigParser()
    cfg_parser.read(_config_paths())

    _load_notifier_config(cfg_parser, cfg)

    accounts_names = set(cfg_parser.sections()) - set([NOTIFIER_SECTION_NAME])
    for account_name in accounts_names:
        username = cfg_parser.get(account_name, 'username')
        plugin_name = cfg_parser.get(account_name, 'type')
        impl = plug_mgr.get_implementation('StatusProvider', plugin_name)
        cfg['accounts'].add(Account(account_name, impl, username))

def _config_paths():
    '''Returns a lis of the config files to read.'''

    paths = xdg.BaseDirectory.load_config_paths('status-notifications.conf')
    return paths

def _load_notifier_config(cfg_parser, cfg):
    '''Loads the notifier configuration part of the config.'''

    if not cfg_parser.has_section(NOTIFIER_SECTION_NAME):
        cfg_parser.add_section(NOTIFIER_SECTION_NAME)
    if cfg_parser.has_option(NOTIFIER_SECTION_NAME, 'times'):
        cfg['times'] = cfg_parser.getint(NOTIFIER_SECTION_NAME, 'times')
    if cfg_parser.has_option(NOTIFIER_SECTION_NAME, 'timeout'):
        cfg['timeout'] = cfg_parser.getint(NOTIFIER_SECTION_NAME, 'timeout')
    if cfg_parser.has_option(NOTIFIER_SECTION_NAME, 'sleep'):
        cfg['sleep'] = cfg_parser.getint(NOTIFIER_SECTION_NAME, 'sleep')
    if cfg_parser.has_option(NOTIFIER_SECTION_NAME, 'notifier'):
        cfg['notifier'] = cfg_parser.get(NOTIFIER_SECTION_NAME, 'notifier')


def _load_config_args(args, cfg):
    '''Parses the arguments and returns the relevant values.'''

    if args.times is not None:
        cfg['times'] = args.times
    if args.timeout is not None:
        cfg['timeout'] = args.timeout
    if args.sleep is not None:
        cfg['sleep'] = args.sleep
    if args.notifier is not None:
        cfg['notifier'] = args.notifier

def _create_arg_parser():
    '''Create the argument parser.'''

    parser = argparse.ArgumentParser(description=__doc__)

    notifier_plugins = list(Notifier.plugin_list())
    def more_than_zero(string):
        val = int(string)
        if val > 0:
            return val
        else:
            raise ValueError(val)

    parser.add_argument('-n', '--notifier', default=None,
                        choices=notifier_plugins,
                        help='The notifier plugin, default is libnotify')
    parser.add_argument('-i', '--times', default=None, type=more_than_zero,
                        help='number of times to show the notification')
    parser.add_argument('-s', '--sleep', default=2, type=more_than_zero,
                        help='Time in seconds to wait between contacts to the'
                             ' server')
    parser.add_argument('-t', '--timeout', default=None, type=int,
                        help='Timeout of the notification')
    parser.add_argument('--config', default=False, action='store_true',
                        help='Open the config menu')
    parser.add_argument('--debug', default='WARNING', choices=_DEBUG_LEVELS,
                        help='Set the logging level')

    return parser

def _save_config(cfg):
    cfg_parser = ConfigParser.ConfigParser()

    cfg_parser.add_section(NOTIFIER_SECTION_NAME)
    cfg_parser.set(NOTIFIER_SECTION_NAME, 'times', cfg['times'])
    cfg_parser.set(NOTIFIER_SECTION_NAME, 'timeout', cfg['timeout'])
    cfg_parser.set(NOTIFIER_SECTION_NAME, 'sleep', cfg['sleep'])
    cfg_parser.set(NOTIFIER_SECTION_NAME, 'notifier', cfg['notifier'])

    for account in cfg['accounts']:
        cfg_parser.add_section(account.name)
        cfg_parser.set(account.name, 'username', account.username)
        cfg_parser.set(account.name, 'type', account.provider.name)
    cfg_dir = xdg.BaseDirectory.save_config_path('')
    cfg_file = open(os.path.join(cfg_dir, 'status-notifications.conf'), 'w')
    cfg_parser.write(cfg_file)
